use anyhow::Result;
use chrono::NaiveDate;
use serde::de::{ self, Visitor };
use serde::{ Deserialize, Serialize, Serializer };
use serde_json::Value;
use std::collections::HashMap;

use crate::REQWEST_CLIENT;
/// Mapping to [fund_all_rate_desc.json](file:///Users/CC/gitee/conanchen/fuquants-workspace/crates/fudacli/x10jqka/sample/fund_all_rate_desc.json)
#[derive(Debug, Serialize, Deserialize)]
pub struct FundAllRate {
    data: FundAllRateData,
    error: Error,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FundAllRateData {
    info: FundAllRateDataInfo,
    data: HashMap<String, FundAllRateDataItem>,
}

#[derive(Debug, Serialize, Deserialize)]
struct FundAllRateDataInfo {
    count: Option<u32>,
    up: Option<u32>,
    down: Option<u32>,
    ping: Option<u32>,
    star: String,
    end: String,
}

/// 基金代码	基金名称	相关链接	2024-02-21(单位净值	累计净值)	2024-02-20(单位净值	累计净值)	增长值	增长率	申购状态	赎回状态	操作
#[derive(Debug, Serialize, Deserialize)]
struct FundAllRateDataItem {
    code: String,
    typename: String,
    net: Option<String>,
    name: String,
    #[serde(rename = "type")]
    type_: String,
    totalnet: Option<String>,
    newnet: String,
    newtotalnet: String,
    newdate: String,
    net1: Option<String>,
    totalnet1: Option<String>,
    ranges: Option<String>,
    rate: Option<String>,
    syrate: Option<String>,
    shstat: String,
    sgstat: String,
    dt: String,
    buy: String,
    clrq: String,
    orgid: String,
    orgname: String,
    #[serde(rename = "SYENDDATE")]
    syenddate: String,
    prerate: String,
    #[serde(rename = "F003N_FUND33")]
    f003n_fund33: String,
    #[serde(rename = "F005")]
    f005: String,
    #[serde(rename = "F008")]
    f008: String,
    #[serde(rename = "F009")]
    f009: String,
    #[serde(rename = "F010")]
    f010: String,
    #[serde(rename = "F011")]
    f011: String,
    #[serde(rename = "F014N_FUND33")]
    f014n_fund33: String,
    #[serde(rename = "F015N_FUND33")]
    f015n_fund33: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Error {
    id: i32,
    msg: String,
}

/// Mapping to [fund_513130_historynet.json](file:///Users/CC/gitee/conanchen/fuquants-workspace/crates/fudacli/x10jqka/sample/fund_513130_historynet.json)
#[derive(Debug, Serialize, Deserialize)]
pub struct FundHistorynet {
    data: Vec<FundHistorynetDataItem>,
    error: Error,
}

#[derive(Debug, Serialize)]
struct FundHistorynetDataItem {
    #[serde(serialize_with = "serialize_naive_date")]
    date: NaiveDate,
    net: f64,
    totalnet: f64,
    fqnet: f64,
    inc: f64,
    rate: f64,
}

fn serialize_naive_date<S>(date: &NaiveDate, serializer: S) -> Result<S::Ok, S::Error>
    where S: Serializer
{
    serializer.serialize_str(&date.format("%Y-%m-%d").to_string())
}

impl<'de> Deserialize<'de> for FundHistorynetDataItem {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
        struct DataItemVisitor;

        impl<'de> Visitor<'de> for DataItemVisitor {
            type Value = FundHistorynetDataItem;

            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("struct DataItem")
            }

            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
                where A: de::MapAccess<'de>
            {
                let mut date = None;
                let mut net = None;
                let mut totalnet = None;
                let mut fqnet = None;
                let mut inc = None;
                let mut rate = None;

                while let Some(key) = map.next_key::<String>()? {
                    match key.as_str() {
                        "date" => {
                            let v_str: String = map.next_value()?;
                            if let Ok(parsed_date) = NaiveDate::parse_from_str(&v_str, "%Y-%m-%d") {
                                date = Some(parsed_date);
                                // println!("Parsed Date: {}", parsed_date);
                            } else {
                                println!("Error parsing the date.");
                            }
                        }
                        "net" => {
                            let v_str: String = map.next_value()?;
                            net = Some(
                                v_str.replace(",", "").parse::<f64>().map_err(de::Error::custom)?
                            );
                        }
                        "totalnet" => {
                            let v_str: String = map.next_value()?;
                            totalnet = Some(
                                v_str.replace(",", "").parse::<f64>().map_err(de::Error::custom)?
                            );
                        }
                        "fqnet" => {
                            let v_str: String = map.next_value()?;
                            fqnet = Some(
                                v_str.replace(",", "").parse::<f64>().map_err(de::Error::custom)?
                            );
                        }
                        "inc" => {
                            let v_str: String = map.next_value()?;
                            inc = Some(
                                v_str.replace(",", "").parse::<f64>().map_err(de::Error::custom)?
                            );
                        }

                        "rate" => {
                            let v_str: String = map.next_value()?;
                            rate = Some(
                                v_str.replace(",", "").parse::<f64>().map_err(de::Error::custom)?
                            );
                        }

                        _ => {
                            let _ = map.next_value::<serde::de::IgnoredAny>();
                        }
                    }
                }

                Ok(FundHistorynetDataItem {
                    date: date.ok_or_else(|| de::Error::missing_field("date"))?,
                    net: net.ok_or_else(|| de::Error::missing_field("net"))?,
                    totalnet: totalnet.ok_or_else(|| de::Error::missing_field("totalnet"))?,
                    fqnet: fqnet.ok_or_else(|| de::Error::missing_field("fqnet"))?,
                    inc: inc.ok_or_else(|| de::Error::missing_field("inc"))?,
                    rate: rate.ok_or_else(|| de::Error::missing_field("rate"))?,
                })
            }
        }

        deserializer.deserialize_map(DataItemVisitor)
    }
}

/// Mapping to [fund_513130_basic.json](/Users/CC/gitee/conanchen/fuquants-workspace/crates/fudacli/x10jqka/sample/fund_513130_basic.json)
#[derive(Debug, Serialize, Deserialize)]
pub struct FundBasic {
    pub data: Vec<FundBasicDataItem>,
    pub error: Error,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FundBasicDataItem {
    pub code: Value,
    pub enddate: Value,
    #[serde(rename = "type")]
    pub type_: Value,
    pub net: Value,
    pub totalnet: Value,
    pub ranges: Value,
    pub rate: Value,
    pub net1: Value,
    pub totalnet1: Value,
    pub enddate1: Value,
    pub mark: Value,
    pub updatetime: Value,
    pub hqcode: Value,
    pub name: Value,
    pub fundtype: Value,
    pub clrq: Value,
    pub manager: Value,
    pub orgname: Value,
    pub dqlcqx: Value,
    pub dqrq: Value,
    pub sgstat: Value,
    pub shstat: Value,
    pub money: Value,
    pub sgoldfl: Value,
    pub sgfl: Value,
    #[serde(rename = "rgStart")]
    pub rg_start: Value,
    #[serde(rename = "rgEnd")]
    pub rg_end: Value,
    #[serde(rename = "rcsgStart")]
    pub rcsg_start: Value,
    #[serde(rename = "rcshStart")]
    pub rcsh_start: Value,
    pub ifnew: Value,
    pub rgfl: Value,
    pub rgflold: Value,
    pub buy: Value,
    pub dt: Value,
    pub zkl: Value,
    pub zdsg: Value,
    pub nowyear: Value,
    pub week: Value,
    pub month: Value,
    pub tmonth: Value,
    pub year: Value,
    pub hyear: Value,
    pub tyear: Value,
    pub mz: Value,
    #[serde(rename = "themeList")]
    pub theme_list: Value,
    pub thsqbfl: Value,
    pub jjgm: Value,
    #[serde(rename = "levelOfRiskCode")]
    pub level_of_risk_code: Value,
    #[serde(rename = "levelOfRisk")]
    pub level_of_risk: Value,
    pub ifzj: Value,
    pub ifgz: Value,
    pub iszcg: Value,
    pub iszcz: Value,
    pub isfof: Value,
    pub asset: Value,
    #[serde(rename = "fundBanner")]
    pub fund_banner: Value,
    #[serde(rename = "showType")]
    pub show_type: Value,
    pub fastcash: Value,
    pub dqlc: Value,
    pub lcqx: Value,
    #[serde(rename = "maxStar")]
    pub max_star: Value,
    pub nowtime: Value,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FundManager {
    pub(super) data: FundManagerData,
    pub error: Error,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FundManagerData {
    now: HashMap<String, FundManagerDataInfo>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FundManagerDataInfo {
    pub code: Value,
    pub fundname: Value,
    pub id: Value,
    pub name: Value,
    pub start: Value,
    pub end: Value,
    pub photo: Value,
    pub intro: Value,
    pub age: Value,
    pub xl: Value,
    #[serde(rename = "shRate")]
    pub sh_rate: Value,
    #[serde(rename = "hsRate")]
    pub hs_rate: Value,
    #[serde(rename = "rate1B0300")]
    pub rate1_b0300: Value,
    #[serde(rename = "rateCommonTypeAvgFqNet")]
    pub rate_common_type_avg_fq_net: Value,
    #[serde(rename = "syInfo")]
    pub sy_info: FundManagerDataInfoSyInfo,
    pub other: HashMap<String, FundManagerDataInfoOther>,
    pub xrjjs: Value,
    pub xrjjzcgm: Value,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FundManagerDataInfoSyInfo {
    pub days: Value,
    pub years: Value,
    pub sy: Value,
    pub avgsy: Value,
    pub cysj: Value,
    pub zdyl: Value,
    pub zdhc: Value,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FundManagerDataInfoOther {
    pub code: Value,
    pub name: Value,
    #[serde(rename = "type")]
    pub type_: Value,
    pub hqcode: Value,
    #[serde(rename = "shRate")]
    pub sh_rate: Value,
    #[serde(rename = "hsRate")]
    pub hs_rate: Value,
    #[serde(rename = "rate1B0300")]
    pub rate1_b0300: Value,
    #[serde(rename = "rateCommonTypeAvgFqNet")]
    pub rate_common_type_avg_fq_net: Value,
    pub start: Value,
    pub end: Value,
    #[serde(rename = "syInfo")]
    pub sy_info: FundManagerDataInfoSyInfo,
}

pub struct Fund;

impl Fund {
    /// Referer: https://fund.10jqka.com.cn/datacenter/jz/
    /// https://fund.10jqka.com.cn/data/Net/info/all_rate_desc_0_0_1_9999_0_0_0_jsonp_g.html
    pub async fn all_rate_desc() -> Result<FundAllRate> {
        // GET /data/Net/info/all_rate_desc_0_0_1_9999_0_0_0_jsonp_g.html HTTP/1.1
        // Accept: text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01
        // Accept-Encoding: gzip, deflate, br
        // Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
        // Cache-Control: no-cache
        // Connection: keep-alive
        // Cookie: Recent=150018; _ga=GA1.1.1109415927.1705805520; _ga_KQBDS1VPQF=GS1.1.1705805519.1.1.1705805558.0.0.0; Hm_lvt_929f8b362150b1f77b477230541dbbc2=1706782074; Hm_lvt_722143063e4892925903024537075d0d=1706782090; Hm_lvt_78c58f01938e4d85eaf619eae71b4ed1=1706782074; spversion=20130314; Hm_lvt_a47da7b82bdb6445aef7aaa2b00470b0=1707193472; Hm_lpvt_a47da7b82bdb6445aef7aaa2b00470b0=1707446991; historystock=000001%7C*%7C688683%7C*%7C300578%7C*%7C002410%7C*%7C838171; Hm_lpvt_722143063e4892925903024537075d0d=1708332588; log=; Hm_lpvt_929f8b362150b1f77b477230541dbbc2=1708332588; v=A4pQoUUh2xYrnle6ZSbKvTg03Xspew7VAP-CeRTDNl1oxyQl_Ate5dCP0ojn; Hm_lpvt_78c58f01938e4d85eaf619eae71b4ed1=1708335382
        // Host: fund.10jqka.com.cn
        // Pragma: no-cache
        // Referer: https://fund.10jqka.com.cn/datacenter/jz/
        // Sec-Fetch-Dest: empty
        // Sec-Fetch-Mode: cors
        // Sec-Fetch-Site: same-origin
        // User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36
        // X-Requested-With: XMLHttpRequest
        // sec-ch-ua: "Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"
        // sec-ch-ua-mobile: ?0
        // sec-ch-ua-platform: "macOS"

        let request_url =
            "https://fund.10jqka.com.cn/data/Net/info/all_rate_desc_0_0_1_9999_0_0_0_jsonp_g.html";
        let response = REQWEST_CLIENT.get(request_url)
            .header(
                reqwest::header::ACCEPT,
                "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01"
            )
            .header(reqwest::header::ACCEPT_ENCODING, "gzip, deflate, br")
            .header(reqwest::header::ACCEPT_LANGUAGE, "zh-CN,zh;q=0.9,en;q=0.8")
            .header(reqwest::header::CACHE_CONTROL, "no-cache")
            .header(reqwest::header::CONNECTION, "keep-alive")
            .header(reqwest::header::HOST, "fund.10jqka.com.cn")
            .header(reqwest::header::PRAGMA, "no-cache")
            .header(reqwest::header::REFERER, "https://fund.10jqka.com.cn/datacenter/jz/")
            .header(
                reqwest::header::USER_AGENT,
                "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
            )
            .header(
                "Sec-Ch-Ua",
                "\"Not A(Brand\";v=\"99\", \"Google Chrome\";v=\"121\", \"Chromium\";v=\"121\""
            )
            .header("Sec-Ch-Ua-Mobile", "?0")
            .header("Sec-Ch-Ua-Platform", "\"macOS\"")
            .header("Sec-Fetch-Dest", "empty")
            .header("Sec-Fetch-Mode", "cors")
            .header("Sec-Fetch-Site", "same-origin")
            .send().await?;

        let body = response.text().await?;

        // let body = r#"g({"data":{"info":{"count":17942,"up":14847,"down":2246,"ping":849,"star":"2024-02-08","end":"2024-02-07"}}})"#;

        // 删除前两个字符和最后一个字符
        let output = &body[2..body.len() - 1];

        // println!("*** output={}\n\nhangqin\n\n", output);

        let fund: FundAllRate = serde_json::from_str(&output)?;

        // println!("{:?}", fund);

        Ok(fund)
    }

    /// Referer: https://fund.10jqka.com.cn/513130/interduce.html#interduce
    /// https://fund.10jqka.com.cn/data/client/myfund/513130
    pub async fn basic(code: &str) -> Result<FundBasic> {
        // GET /data/client/myfund/513130 HTTP/1.1
        // Accept: application/json, text/javascript, */*; q=0.01
        // Accept-Encoding: gzip, deflate, br
        // Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
        // Cache-Control: no-cache
        // Connection: keep-alive
        // Cookie: Recent=150018; _ga=GA1.1.1109415927.1705805520; _ga_KQBDS1VPQF=GS1.1.1705805519.1.1.1705805558.0.0.0; Hm_lvt_929f8b362150b1f77b477230541dbbc2=1706782074; Hm_lvt_722143063e4892925903024537075d0d=1706782090; Hm_lvt_78c58f01938e4d85eaf619eae71b4ed1=1706782074; spversion=20130314; Hm_lvt_a47da7b82bdb6445aef7aaa2b00470b0=1707193472; Hm_lpvt_a47da7b82bdb6445aef7aaa2b00470b0=1707446991; Hm_lpvt_722143063e4892925903024537075d0d=1708336190; Hm_lpvt_929f8b362150b1f77b477230541dbbc2=1708336191; historystock=688347%7C*%7C300578%7C*%7C300068%7C*%7C300270; Hm_lpvt_78c58f01938e4d85eaf619eae71b4ed1=1708649592; v=A5ZM_dFt770CUdsZboQemeQw4UedN9pxLHsO1QD_gnkUwzj5aMcqgfwLXuHT
        // Host: fund.10jqka.com.cn
        // Pragma: no-cache
        // Referer: https://fund.10jqka.com.cn/513130/interduce.html
        // Sec-Fetch-Dest: empty
        // Sec-Fetch-Mode: cors
        // Sec-Fetch-Site: same-origin
        // User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36
        // X-Requested-With: XMLHttpRequest
        // hexin-v: A5ZM_dFt770CUdsZboQemeQw4UedN9pxLHsO1QD_gnkUwzj5aMcqgfwLXuHT
        // sec-ch-ua: "Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"
        // sec-ch-ua-mobile: ?0

        let request_url = format!("https://fund.10jqka.com.cn/data/client/myfund/{code}");
        let response = REQWEST_CLIENT.get(request_url)
            .header(reqwest::header::ACCEPT, "application/json, text/javascript, */*; q=0.01")
            .header(reqwest::header::ACCEPT_ENCODING, "gzip, deflate, br")
            .header(reqwest::header::ACCEPT_LANGUAGE, "zh-CN,zh;q=0.9,en;q=0.8")
            .header(reqwest::header::CACHE_CONTROL, "no-cache")
            .header(reqwest::header::CONNECTION, "keep-alive")
            .header(reqwest::header::HOST, "fund.10jqka.com.cn")
            .header(reqwest::header::PRAGMA, "no-cache")
            .header(reqwest::header::REFERER, "https://fund.10jqka.com.cn/513130/interduce.html")
            .header(
                reqwest::header::USER_AGENT,
                "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
            )
            .header(
                "Sec-Ch-Ua",
                "\"Not A(Brand\";v=\"99\", \"Google Chrome\";v=\"121\", \"Chromium\";v=\"121\""
            )
            .header("Sec-Ch-Ua-Mobile", "?0")
            .header("Sec-Ch-Ua-Platform", "\"macOS\"")
            .header("Sec-Fetch-Dest", "empty")
            .header("Sec-Fetch-Mode", "cors")
            .header("Sec-Fetch-Site", "same-origin")
            .send().await?;

        let body: String = response.text().await?;

        println!("*** body={}\n", body);

        let fund: FundBasic = serde_json::from_str(&body)?;

        // println!("{:?}", fund);

        Ok(fund)
    }

    /// Referer: https://fund.10jqka.com.cn/513130/interduce.html#manager
    /// https://fund.10jqka.com.cn/interface/fund/managerInfo?code=513130
    pub async fn managers(code: &str) -> Result<FundManager> {
        let request_url = format!(
            "https://fund.10jqka.com.cn/interface/fund/managerInfo?code={code}"
        );

        let response = REQWEST_CLIENT.get(request_url)
            .header(reqwest::header::ACCEPT, "application/json, text/javascript, */*; q=0.01")
            .header(reqwest::header::ACCEPT_ENCODING, "gzip, deflate, br")
            .header(reqwest::header::ACCEPT_LANGUAGE, "zh-CN,zh;q=0.9,en;q=0.8")
            .header(reqwest::header::CACHE_CONTROL, "no-cache")
            .header(reqwest::header::CONNECTION, "keep-alive")
            .header(reqwest::header::HOST, "fund.10jqka.com.cn")
            .header(reqwest::header::PRAGMA, "no-cache")
            .header(reqwest::header::REFERER, "https://fund.10jqka.com.cn/513130/interduce.html")
            .header(
                reqwest::header::USER_AGENT,
                "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
            )
            .header(
                "Sec-Ch-Ua",
                "\"Not A(Brand\";v=\"99\", \"Google Chrome\";v=\"121\", \"Chromium\";v=\"121\""
            )
            .header("Sec-Ch-Ua-Mobile", "?0")
            .header("Sec-Ch-Ua-Platform", "\"macOS\"")
            .header("Sec-Fetch-Dest", "empty")
            .header("Sec-Fetch-Mode", "cors")
            .header("Sec-Fetch-Site", "same-origin")
            .send().await?;

        let body: String = response.text().await?;

        // println!("*** body={}\n", body);

        let fund: FundManager = serde_json::from_str(&body)?;

        // println!("{:?}", fund);

        Ok(fund)
    }

    pub async fn historynet(code: &str) -> Result<FundHistorynet> {
        // GET /dockercache/fund/historynet/513130 HTTP/1.1
        // Accept: */*
        // Accept-Encoding: gzip, deflate, br
        // Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
        // Cache-Control: no-cache
        // Connection: keep-alive
        // Cookie: Recent=150018; _ga=GA1.1.1109415927.1705805520; _ga_KQBDS1VPQF=GS1.1.1705805519.1.1.1705805558.0.0.0; Hm_lvt_929f8b362150b1f77b477230541dbbc2=1706782074; Hm_lvt_722143063e4892925903024537075d0d=1706782090; Hm_lvt_78c58f01938e4d85eaf619eae71b4ed1=1706782074; spversion=20130314; Hm_lvt_a47da7b82bdb6445aef7aaa2b00470b0=1707193472; Hm_lpvt_a47da7b82bdb6445aef7aaa2b00470b0=1707446991; historystock=000001%7C*%7C688683%7C*%7C300578%7C*%7C002410%7C*%7C838171; Hm_lpvt_722143063e4892925903024537075d0d=1708336190; Hm_lpvt_929f8b362150b1f77b477230541dbbc2=1708336191; Hm_lpvt_78c58f01938e4d85eaf619eae71b4ed1=1708336191; v=A2e9ijjWzqqNxEomI2TfVq1b8JA07DvLlcC_QjnUg_YdKIlOQbzLHqWQT5dK
        // Host: fund.10jqka.com.cn
        // Pragma: no-cache
        // Referer: https://fund.10jqka.com.cn/513130/historynet.html
        // Sec-Fetch-Dest: empty
        // Sec-Fetch-Mode: cors
        // Sec-Fetch-Site: same-origin
        // User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36
        // hexin-v: A2e9ijjWzqqNxEomI2TfVq1b8JA07DvLlcC_QjnUg_YdKIlOQbzLHqWQT5dK
        // sec-ch-ua: "Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"
        // sec-ch-ua-mobile: ?0
        // sec-ch-ua-platform: "macOS"

        let request_url =
            format!("https://fund.10jqka.com.cn/dockercache/fund/historynet/{}", code);
        let response = REQWEST_CLIENT.get(request_url)
            .header(reqwest::header::ACCEPT, "*/*")
            .header(reqwest::header::ACCEPT_ENCODING, "gzip, deflate, br")
            .header(reqwest::header::ACCEPT_LANGUAGE, "zh-CN,zh;q=0.9,en;q=0.8")
            .header(reqwest::header::CACHE_CONTROL, "no-cache")
            .header(reqwest::header::CONNECTION, "keep-alive")
            .header(reqwest::header::HOST, "fund.10jqka.com.cn")
            .header(reqwest::header::PRAGMA, "no-cache")
            .header(
                reqwest::header::REFERER,
                format!("https://fund.10jqka.com.cn/{}/historynet.html", code)
            )
            .header(
                reqwest::header::USER_AGENT,
                "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
            )
            .header(
                "Sec-Ch-Ua",
                "\"Not A(Brand\";v=\"99\", \"Google Chrome\";v=\"121\", \"Chromium\";v=\"121\""
            )
            .header("Sec-Ch-Ua-Mobile", "?0")
            .header("Sec-Ch-Ua-Platform", "\"macOS\"")
            .header("Sec-Fetch-Dest", "empty")
            .header("Sec-Fetch-Mode", "cors")
            .header("Sec-Fetch-Site", "same-origin")
            .send().await?;

        let body: String = response.text().await?; // Use bytes() instead of text() to get the raw response body

        // println!("*** body={}\n\nhangqin\n\n", body);

        let fund_historynet: FundHistorynet = serde_json::from_str(&body)?;

        // println!("{:?}", fund);

        Ok(fund_historynet)
    }
}

pub fn add_two_numbers(a: i32, b: i32) -> i32 {
    a + b
}

#[cfg(test)]
mod test {
    use std::{ fs::File, io::BufReader };

    use super::*;

    #[tokio::test]
    async fn parse_funds_json_file() {
        let path =
            "/Users/CC/gitee/conanchen/fuquants-workspace/crates/fudacli/x10jqka/sample/fund_all_rate_desc.json";

        // Open the JSON path and read its contents
        let path = File::open(path).expect(&format!("Failed to open path={}", path));
        let reader = BufReader::new(path);

        // Parse the JSON data into the Data struct
        let fund: FundAllRate = serde_json::from_reader(reader).expect("Failed to parse JSON");

        println!("{:?}", fund.data.data.get("f513130"));
        assert_eq!(fund.data.data.len(), 1);
    }

    #[tokio::test]
    async fn parse_fund_historynet_json_file() {
        let path =
            "/Users/CC/gitee/conanchen/fuquants-workspace/crates/fudacli/x10jqka/sample/fund_513130_historynet.json";
        // Open the JSON path and read its contents
        let path = File::open(path).expect(&format!("Failed to open path={}", path));
        let reader = BufReader::new(path);

        // Parse the JSON data into the Data struct
        let fund_historynet: FundHistorynet = serde_json
            ::from_reader(reader)
            .expect("Failed to parse JSON");

        assert_eq!(fund_historynet.data.len(), 3);
        assert_eq!(fund_historynet.data.first().is_some(), true);
        if let Some(first) = fund_historynet.data.first() {
            println!("first={:?}", first);
            assert_eq!(first.date, NaiveDate::from_ymd_opt(2024, 02, 08).unwrap_or_default());
            assert_eq!(first.net, 0.4205);
            assert_eq!(first.totalnet, 0.4205);
            assert_eq!(first.fqnet, 0.4205);
            assert_eq!(first.inc, -0.0022);
            assert_eq!(first.rate, -0.52);
        }
    }

    #[tokio::test]
    async fn test_fund_all_rate_desc() {
        let result = Fund::all_rate_desc().await;

        assert_eq!(result.is_ok(), true);
        if let Ok(fund) = result {
            println!("fund.data.info= {:?}", fund.data.info);
            println!("\nfund.data.data.last= {:?}", fund.data.data.iter().last());
            assert_eq!(fund.data.data.len() > 1, true);
        }
    }

    #[tokio::test]
    async fn test_fund_basic() {
        let result = Fund::basic("513130").await;

        assert_eq!(result.is_ok(), true);
        if let Ok(fund_basic) = result {
            println!("fund_basic.data[0]={:?}", fund_basic.data.first());
            assert_eq!(fund_basic.data.len() == 1, true);
        }
    }

    #[tokio::test]
    async fn test_fund_manager_info() {
        let result = Fund::managers("513130").await;

        assert_eq!(result.is_ok(), true);
        if let Ok(fund_manager) = result {
            assert_eq!(fund_manager.data.now.len() > 1, true);
            println!("fund_manager.data.now.keys= {:?}", fund_manager.data.now.keys());
        }
    }

    #[tokio::test]
    async fn test_fund_historynet() {
        let result = Fund::historynet("513130").await;

        assert_eq!(result.is_ok(), true);
        if let Ok(fund_historynet) = result {
            println!("fund_historynet.data[0]={:?}", fund_historynet.data.first());
            assert_eq!(fund_historynet.data.len() > 1, true);
        }
    }

    #[tokio::test]
    async fn adds_two_positive_numbers() {
        let result = add_two_numbers(2, 3);
        assert_eq!(result, 5);
    }

    #[tokio::test]
    async fn adds_two_negative_numbers() {
        let result = add_two_numbers(-2, -3);
        assert_eq!(result, -5);
    }

    #[tokio::test]
    async fn adds_positive_and_negative_number() {
        let result = add_two_numbers(2, -3);
        assert_eq!(result, -1);
    }
}
